'use strict';
const { resolve, join } = require('path');
const { removeSync, ensureDirSync, copySync, existsSync, writeFileSync } = require('fs-extra');
const inquirer = require('inquirer');
const semver = require('semver');
const userHome = require('user-home');
const glob = require('glob');
const ejs = require('ejs');
const Command = require('@ysx-cli/command');
const log = require('@ysx-cli/log');
const Package = require('@ysx-cli/package');
const { spinnerStart, smartSpawnSync } = require('@ysx-cli/utils');
const {templatesMap} = require("./templates");


const CREATE_TYPE = {
  project: 'project',
  component: 'component'
};

const WHITE_COMMANDS = ['yarn', 'npm'];

class CreateCommand extends Command {
  constructor(argv) {
    super(argv);
  }
  init() {
    this.projectName = this.argv[0];
    this.options = this.argv[1];
    // argv[2] 没有传进来
    log.verbose('init verbose', this.projectName, this.options);
    // process.cwd(); // 等于 resolve('.')
    this.targetPath = process.cwd() + '/' + this.projectName;
  }
  async exec() {
    try {
      const projectInfo = await this.prepare();
      if (projectInfo) {
        this.projectInfo = projectInfo;
        await this.downloadTpl();
        await this.installTpl();
      }
    } catch (error) {
      log.error(error.message);
    }
  }
  async downloadTpl() {
    // console.log('download', this.projectInfo);
    this.templateInfo = this.templates.find(item => item.name === this.projectInfo.templateName);

    const targetPath = resolve(userHome, '.ysx-cli', 'template');
    const storeDir = join(targetPath, 'node_modules');
    this.templatePackage = new Package({
      targetPath,
      storeDir,
      packageName: this.templateInfo.pkgName,
      packageVersion: this.templateInfo.version
    });
    // console.log('templatePackage', templatePackage);
    const spinner = spinnerStart('正在加载模板...');
    if (await this.templatePackage.exists()) {
      await this.templatePackage.update();
    } else {
      await this.templatePackage.install();
    }
    spinner.stop(true);
    log.success('模板加载成功');
  }

  async installTpl() {
    // console.log('installTpl cacheFilePath', this.templatePackage.cacheFilePath);
    if (this.templateInfo) {
      await this.installNormalTpl();
    } else {
      throw new Error('模板信息不存在');
    }
  }

  async installNormalTpl() {
    const spinner = spinnerStart('正在安装模板...');
    const templatePath = join(this.templatePackage.cacheFilePath + '/template');

    // 不存在就自动创建
    ensureDirSync(templatePath);
    ensureDirSync(this.targetPath);
    copySync(templatePath, this.targetPath);
    spinner.stop(true);
    log.success('模板安装成功');
    await this.renderTpl();
    await this.serveProject();
  }

  async renderTpl() {
    return new Promise((resolve, reject) => {
      glob('**', {
        cwd: this.targetPath,
        ignore: this.templateInfo.ignore,
        nodir: true
      }, (error, files) => {
        if (error) {
          reject(error);
        } else {
          Promise.all(files.map(file => {
            const filePath = join(this.targetPath, file);
            return new Promise((success, fail) => {
              // console.log('projectInfo>>>', this.projectInfo);
              ejs.renderFile(filePath, {
                projectName: this.projectInfo.name,
                version: this.projectInfo.version,
                description: this.projectInfo.description
              }, (err, content) => {
                if (err) {
                  fail(err);
                } else {
                  // console.log('content', content);
                  writeFileSync(filePath, content);
                  success(content);
                }
              });
            });
          })).then(() => {
            resolve();
          }).catch(err => {
            reject(err);
          });
        }
      });
    });
  }

  async serveProject() {
    // console.log(this.templateInfo);
    const { installCmd, serveCmd } = this.templateInfo;
    let installSuccess = false;
    if (installCmd) {
      let installCmdArr = installCmd.split(' ');
      const cmd = installCmdArr[0];
      const args = installCmdArr.slice(1);
      if (this.checkCmd(cmd)) {
        const ret = await smartSpawnSync(cmd, args, {
          cwd: this.targetPath,
          stdio: 'inherit'
        });
        if (ret === 0) {
          log.success('依赖安装成功');
          installSuccess = true;
        } else {
          throw new Error('依赖安装失败');
        }
      }
    }
    if (installSuccess && serveCmd) {
      let serveCmdArr = serveCmd.split(' ');
      const cmd = serveCmdArr[0];
      const args = serveCmdArr.slice(1);
      if (this.checkCmd(cmd)) {
        const ret = await smartSpawnSync(cmd, args, {
          cwd: this.targetPath,
          stdio: 'inherit'
        });
        if (ret === 0) {
          log.success('项目启动成功');
        } else {
          throw new Error('项目启动失败');
        }
      }
    }
  }

/*  async execCmd() {

  }*/

  async prepare() {
    // 这步应该请求后端
    this.templates = templatesMap;
    if (!this.templates.length) {
      throw new Error('模板不存在');
    }
    let goon = true;
    if (existsSync(this.targetPath)) {
      if (this.options.force) {
        removeSync(this.targetPath);
      } else {
        const answers = await inquirer.prompt({
          continue: {
            type: 'confirm',
            message: '文件夹已存在, 继续将删除已有文件夹，是否继续?',
            default: false
          }
        });
        if (answers.continue) {
          removeSync(this.targetPath);
        } else {
          goon = false;
        }
      }
    }
    return goon ? this.getProjectInfo() : null;
  }
  async getProjectInfo() {
    let result = {};
    const { type } = await inquirer.prompt({
      type: {
        type: 'list',
        message: '请选择项目类型?',
        default: CREATE_TYPE.project,
        choices: [
          { name: '项目', value: CREATE_TYPE.project },
          { name: '组件', value: CREATE_TYPE.component }
        ]
      }
    });
    const that = this;
    const title = type === CREATE_TYPE.component ? '组件' : '项目';
    const info = await inquirer.prompt({
      name: {
        type: 'input',
        message: '请输入' + title + '名称',
        when() {
          return !that.projectName;
        },
        validate(value) {
          // 字母开头, 字母或数字结尾，特殊符合只允许 - _
          const done = this.async();
          if (/^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]|[a-zA-Z0-9])*$/.test(value)) {
            done(null, true);
          } else {
            done('名称不合法');
          }
        }
      },
      version: {
        type: 'input',
        message: '请输入' + title + '版本号',
        default: '0.0.1',
        validate(value) {
          const done = this.async();
          if (semver.valid(value)) {
            done(null, true);
          } else {
            done('版本号不合法');
          }
          // return !!semver.valid(value); // 一定要返回boolean
        }
      },
      templateName: {
        type: 'list',
        message: '请选择' + title + '模板',
        choices: this.createTemplateChoices(type)
      }
    });
    result = {
      ...info,
      type,
      name: this.projectName || info.name
      // className  视频里把name变成了 横杠形式，后面作为新项目 package.json的name
    }
    if (title === '组件') {
      const { description } = await inquirer.prompt({
        description: {
          type: 'input',
          message: '描述信息?',
          validate(value) {
            const done = this.async();
            if (value) {
              done(null, true);
            } else {
              done('请填写描述信息');
            }
          }
        }
      });
      result.description = description;
    }
    // console.log('result>>', result);
    return result;
  }
  createTemplateChoices(type) {
    return this.templates.filter(item => item.tag === type).map(item => {
      return {
        name: item.name,
        value: item.name
      }
    });
  }
  checkCmd(cmd) {
    return WHITE_COMMANDS.includes(cmd);
  }
}


function create(argv) {
  return new CreateCommand(argv);
}

module.exports = create;
